]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
Add a concept of "going modal" by grabbing the keyboard
authorOwen Taylor <otaylor@src.gnome.org>
Mon, 24 Nov 2008 19:07:18 +0000 (19:07 +0000)
committerOwen Taylor <otaylor@src.gnome.org>
Mon, 24 Nov 2008 19:07:18 +0000 (19:07 +0000)
shell-global.[ch]: Replace shell_global_focus_stage()
  with shell_global_grab_keyboard()/shell_global_ungrab_keyboard()
main.js: Add startModal()/endModal() functions to go modal and
  undo that.
run_dialog.js overlay.js main.js: Use startModal() for the overlay
  and for the run dialog.

http://bugzilla.gnome.org/show_bug.cgi?id=561880

svn path=/trunk/; revision=83

js/ui/main.js
js/ui/overlay.js
js/ui/run_dialog.js
src/shell-global.c
src/shell-global.h

index c52a2460b10a2048b3ed2dc76cfe5eb31b47e8a1..b2217906d70927ae0131e58a3785592208820170 100644 (file)
@@ -103,13 +103,14 @@ function start() {
         // Make sure not more than one run dialog is shown.
         if (!run_dialog) {
             run_dialog = new RunDialog.RunDialog();
-           let handler = function() {
+           let end_handler = function() {
                 run_dialog.destroy();
                 run_dialog = null;
             };
-            run_dialog.connect('run', handler);
-            run_dialog.connect('cancel', handler);
-            run_dialog.show();
+            run_dialog.connect('run', end_handler);
+            run_dialog.connect('cancel', end_handler);
+            if (!run_dialog.show())
+               end_handler();
         }
     });
 
@@ -120,17 +121,33 @@ function start() {
     wm = new WindowManager.WindowManager();
 }
 
-function show_overlay() {
+// Used to go into a mode where all keyboard and mouse input goes to
+// the stage. Returns true if we successfully grabbed the keyboard and
+// went modal, false otherwise
+function startModal() {
     let global = Shell.global_get();
 
-    overlay.show();
+    if (!global.grab_keyboard())
+       return false;
+
     global.set_stage_input_area(0, 0, global.screen_width, global.screen_height);
+
+    return true;
 }
 
-function hide_overlay() {
+function endModal() {
     let global = Shell.global_get();
 
-    overlay.hide();
-    panel.overlayHidden();
+    global.ungrab_keyboard();
     global.set_stage_input_area(0, 0, global.screen_width, Panel.PANEL_HEIGHT);
 }
+
+function show_overlay() {
+    if (startModal())
+       overlay.show();
+}
+
+function hide_overlay() {
+    overlay.hide();
+    endModal();
+}
index e15ed991f159bb80654b0772b79033771c2a7086..827d36660d5c672f96b1eae8062bf7b7cf2570e2 100644 (file)
@@ -178,8 +178,6 @@ Overlay.prototype = {
 
            let global = Shell.global_get();
 
-           global.focus_stage();
-
            let windows = global.get_windows();
            let desktopWindow = null;
 
index 9c9fa2078dcb42e06a2162886de68c8bc05b74ba..aa8beaa2653c1682fb2742552379f72b4ea27709 100644 (file)
@@ -26,8 +26,9 @@ RunDialog.prototype = {
     _init : function() {
         let global = Shell.global_get();
 
-        // All actors are inside _group.
-        this._group = new Clutter.Group();
+        // All actors are inside _group. We create it initially
+       // hidden then show it in show()
+        this._group = new Clutter.Group({ visible: false });
         global.stage.add_actor(this._group);
 
         this._overlay = new Clutter.Rectangle({ color: OVERLAY_COLOR,
@@ -74,19 +75,6 @@ RunDialog.prototype = {
             return false;
         });
 
-        // TODO: Detect escape key and make it cancel the operation.
-        //       Use me.on_cancel() if it exists. Something like this:
-        // this._entry.connect('key-press-event', function(o, e) {
-        //     if (the pressed key is the escape key) {
-        //         me.hide();
-        //         me.emit('cancel');
-        //         return false;
-        //     } else
-        //         return true;
-        // });
-
-        global.focus_stage();
-        global.stage.set_key_focus(this._entry);
     },
 
     _run : function(command) {
@@ -104,14 +92,41 @@ RunDialog.prototype = {
     },
 
     show : function() {
+       if (this._group.visible) // Already shown
+           return false;
+
+       if (!Main.startModal())
+           return false;
+
         this._group.show_all();
+
+        // TODO: Detect escape key and make it cancel the operation.
+        //       Use me.on_cancel() if it exists. Something like this:
+        // this._entry.connect('key-press-event', function(o, e) {
+        //     if (the pressed key is the escape key) {
+        //         me.hide();
+        //         me.emit('cancel');
+        //         return false;
+        //     } else
+        //         return true;
+        // });
+
+       let global = Shell.global_get();
+        global.stage.set_key_focus(this._entry);
+
+       return true;
     },
 
     hide : function() {
-        this._group.hide();
+       if (!this._group.visible)
+           return;
+
+       this._group.hide();
+       Main.endModal();
     },
 
     destroy : function(){
+       this.hide();
         this._group.destroy();
     }
 };
index d469c98750dc564991ec01490e89b6b7c67f4790..55b8378599130659afdcbb1c413b3f9f1261ff08 100644 (file)
@@ -11,6 +11,7 @@ struct _ShellGlobal {
 
   MutterPlugin *plugin;
   ShellWM *wm;
+  gboolean keyboard_grabbed;
 };
 
 enum {
@@ -273,19 +274,64 @@ _shell_global_set_plugin (ShellGlobal  *global,
 }
 
 /**
- * shell_global_focus_stage:
+ * shell_global_grab_keyboard:
+ * @global: a #ShellGlobal
  *
- * Set the keyboard focus to the Clutter stage window.  This function
- * is best used in combination with some sort of visual notification
- * that the shell has taken over input.
+ * Grab the keyboard to the stage window. The stage will receive
+ * all keyboard events until shell_global_ungrab_keyboard() is called.
+ * This is appropriate to do when the desktop goes into a special
+ * mode where no normal global key shortcuts or application keyboard
+ * processing should happen.
  */
-void
-shell_global_focus_stage (ShellGlobal *global)
+gboolean
+shell_global_grab_keyboard (ShellGlobal *global)
 {
   MetaScreen *screen = mutter_plugin_get_screen (global->plugin);
   MetaDisplay *display = meta_screen_get_display (screen);
   Display *xdisplay = meta_display_get_xdisplay (display);
   ClutterStage *stage = CLUTTER_STAGE (mutter_plugin_get_stage (global->plugin));
   Window stagewin = clutter_x11_get_stage_window (stage);
-  XSetInputFocus (xdisplay, stagewin, RevertToParent, CurrentTime);
+
+  /* FIXME: we need to coordinate with the rest of Metacity or we
+   * may grab the keyboard away from other portions of Metacity
+   * and leave Metacity in a confused state. An X client is allowed
+   * to overgrab itself, though not allowed to grab they keyboard
+   * away from another applications.
+   */
+  if (global->keyboard_grabbed)
+    return FALSE;
+
+  if (XGrabKeyboard (xdisplay, stagewin,
+                     False, /* owner_events - steal events from the rest of metacity */
+                     GrabModeAsync, GrabModeAsync,
+                     CurrentTime) != Success)
+    return FALSE; /* probably AlreadyGrabbed, some other app has a keyboard grab */
+
+  global->keyboard_grabbed = TRUE;
+
+  return TRUE;
+}
+
+/**
+ * shell_global_ungrab_keyboard:
+ * @global: a #ShellGlobal
+ *
+ * Undoes the effect of shell_global_grab_keyboard
+ */
+void
+shell_global_ungrab_keyboard (ShellGlobal *global)
+{
+  MetaScreen *screen;
+  MetaDisplay *display;
+  Display *xdisplay;
+
+  g_return_if_fail (global->keyboard_grabbed);
+
+  screen = mutter_plugin_get_screen (global->plugin);
+  display = meta_screen_get_display (screen);
+  xdisplay = meta_display_get_xdisplay (display);
+
+  XUngrabKeyboard (xdisplay, CurrentTime);
+
+  global->keyboard_grabbed = FALSE;
 }
index d85796234150b04f218a4f89a61bf9d1ee7f72a7..1970b0ac4967ca869181e30e3cc80104494a8d6f 100644 (file)
@@ -49,7 +49,8 @@ void _shell_global_set_plugin (ShellGlobal  *global,
 
 MetaScreen * shell_global_get_screen (ShellGlobal *global);
 
-void shell_global_focus_stage (ShellGlobal *global);
+gboolean shell_global_grab_keyboard   (ShellGlobal *global);
+void     shell_global_ungrab_keyboard (ShellGlobal *global);
 
 G_END_DECLS